#include "setter.h"

#include <linux/nl80211.h>
#include <netlink/attr.h>
#include <netlink/genl/genl.h>
#include <netlink/msg.h>
#include <netlink/netlink.h>
#include <netlink/socket.h>

#include "log.h"
#include "monitor.h"

static int oldpwr = 15;
static int minpwr = 6;
int maxpwr = 15;
static int minstrength = 15;
static int maxstrenght = 80;
static int minchange = 40;
static int maxchange = 55;

int setter(int oldval, int newval, Wifi *w) {
    log_out_v("SETTER", "setter called %i -> %i\n", oldval, newval);
    Netlink nl;

    nl.id = initNl80211(&nl, w, NULL);
    if (nl.id < 0) {
        log_err("SETTER", "Error initializing netlink 802.11\n");
        exit(EXIT_FAILURE);
    }

    int txpwr = calculate_pwr(oldpwr, newval);
    oldpwr = txpwr;

    // xxx txpwr in dBm to kernel in mBm
    int result = setpwr(&nl, w, 100 * txpwr);
    nl_close(nl.socket);
    nl_socket_free(nl.socket);
    return result;
}

static int setpwr(Netlink *nl, Wifi *w, int txpwr) {
    nl->result = 1;

    struct nl_msg* msg = nlmsg_alloc();

    if (!msg) {
        log_err("SETPWR", "Failed to allocate netlink message.\n");
        return -1;
    }

    if (genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl->id, 0, 0,
                NL80211_CMD_SET_WIPHY, 0) == NULL) {
        log_err("SETPWR", "Error creating message\n");
        return -1;
    }


    nla_put_u32(msg, NL80211_ATTR_IFINDEX, w->ifindex);
    nla_put_u32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, NL80211_TX_POWER_FIXED);
    nla_put_u32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, txpwr);
    int result = nl_send_auto(nl->socket, msg);
    if (result < 0) {
        log_err("SETPWR", "Error sending request\n");
        return -1;
    }
    nlmsg_free(msg);

    return 1;
}

static int calculate_pwr(int oldpwr, int newval) {
    if (newval > maxchange || newval < minchange) {
        float current_state = (float)(newval - minstrength) / (maxstrenght - minstrength);
        float multplier = .5 / current_state;
        int newpwr = oldpwr * multplier;
        log_out("CALCULATE_PWR", "calculated newpwr as %i\n", newpwr);
        if (newpwr > maxpwr)
            return maxpwr;
        if (newpwr < minpwr)
            return minpwr;
        return newpwr;
    }
    log_out("CALCULATE_PWR", "not changing\n");
    return oldpwr;
}
